﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;

namespace AudioEffectBox
{
    public class AudioEffectGenerator
    {
        public const int SAMPLING_RATE = 22050;
        private const int BUFFER_SIZE = SAMPLING_RATE * 2 * 2;//2 seconds buffer @ 16 bit/sample
       
        private int _echoDelay;       
        private int _echoOffset;
        

        
        private int _ReverberationDelay;        
        private int _ReverberationOffset;

                
        private ModulationType _modulationType;
        private int _modulationLevel;

        private Audio16MixStream _EchoMixer;
        private Audio16MixStream _ReverberationMixer;
        private Audio16ModulationStream _ModulationMixer;
        private Audio16StereoBalancerStream _balancerMixer;
        
        private BackupStream _IntermediaryOutputStream;
        private Audio16BufferStream _OutputStream;

        private Stream _EchoStream;
        private Stream _ReverberationStream;        
        private Stream _ModulationStream;
        private Stream _storeStream;
        private Stream _BalancerStream;

        private byte[] _rawBuffer;

        public AudioEffectGenerator()
        {
            _rawBuffer = new byte[BUFFER_SIZE]; // init Echo / Reverb Buffer
            Array.Clear(_rawBuffer, 0, BUFFER_SIZE);

            InitParameters();                        
            _modulationLevel = 50;
            
            _storeStream = new CyclingStream(new MemoryStream(_rawBuffer));
            _EchoStream =  new CyclingStream(new MemoryStream(_rawBuffer));
            _ReverberationStream =  new CyclingStream(new MemoryStream(_rawBuffer));
            _ModulationStream = new CyclingStream(new MemoryStream(BuildModulationWaveTable(ModulationType.Sinus,_modulationLevel)));
            _BalancerStream = new CyclingStream(new MemoryStream(BuildModulationWaveTable(ModulationType.Sinus,50)));

            _echoOffset = (_echoDelay * SAMPLING_RATE / 10) * 2;
            _ReverberationOffset = (_ReverberationDelay * SAMPLING_RATE / 1000) * 2;

            int _maxOffset = Math.Max(_echoOffset, _ReverberationOffset);
            _storeStream.Seek(_maxOffset, SeekOrigin.Begin);
            _EchoStream.Seek(_maxOffset - _echoOffset, SeekOrigin.Begin);
            _ReverberationStream.Seek(_maxOffset - _ReverberationOffset, SeekOrigin.Begin);

            _OutputStream = new Audio16BufferStream(); //Output Buffer
            _balancerMixer = new Audio16StereoBalancerStream(_BalancerStream, 100, _OutputStream, -275); //Stereo Balancer
            _IntermediaryOutputStream = new BackupStream(_balancerMixer, _storeStream); // Backup for reverb & echo
            _EchoMixer = new Audio16MixStream(_EchoStream, 30, _IntermediaryOutputStream); //Echo
            _ReverberationMixer = new Audio16MixStream(_ReverberationStream, 30, _EchoMixer); //Reverb        
            _ModulationMixer = new Audio16ModulationStream(_ModulationStream, 100,_ReverberationMixer , 0); //Modulation

            _ModulationMixer.IsEnabled = false;
            _EchoMixer.IsEnabled = false;
            _ModulationMixer.IsEnabled = false;
            _balancerMixer.IsEnabled = false;
            
        }

        private void InitParameters()
        {
            
            _echoDelay = 10; //10*100 ms = 1s;            
            _ReverberationDelay = 10; // 10* 1ms = 10ms              
            _modulationType = ModulationType.Sinus;
        }
        
        private byte[] BuildModulationWaveTable(ModulationType modulationType,int modulationLevel)
        {
            byte[] _modulationBytes = new byte[400];
            Stream _stream = new MemoryStream(_modulationBytes);
            BinaryWriter _writer = new BinaryWriter(_stream);
            int _midLenght = _modulationBytes.Length / 4;
            for (int i = 0; i < _modulationBytes.Length/2; i++)
            {
                UInt16 _sample = 0;
                switch (modulationType)
                {
                    case ModulationType.Sinus:
                        _sample = (UInt16)(Math.Sin(Math.PI * i*4 / _modulationBytes.Length) * modulationLevel +(100 - modulationLevel));
                        break;
                    case ModulationType.Square:                        
                        _sample = (UInt16)((i>_midLenght)? 100-2*modulationLevel:100);
                        break;
                    case ModulationType.Saw:
                        _sample = (UInt16)((i > _midLenght) ? 100 - 2 * modulationLevel : (modulationLevel *2* i / _midLenght +(100- 2*modulationLevel)));
                        break;                    
                }
                
                _writer.Write(_sample);
            }
            return _modulationBytes;
        }


        private void SetReverberationDelay(int value)
        {
            lock (this._ReverberationMixer)
            {
                _ReverberationDelay = value;
                _ReverberationOffset = (_ReverberationDelay * SAMPLING_RATE / 1000) * 2;
                int _posToSeek = (int)(_storeStream.Position - _ReverberationOffset);
                if (_posToSeek < 0) _posToSeek = (BUFFER_SIZE + _posToSeek);
                _ReverberationStream.Seek(_posToSeek, SeekOrigin.Begin);

            }
        }

        private void SetEchoDelay(int value)
        {
            lock (this._EchoMixer)
            {
                _echoDelay = value;
                _echoOffset = (_echoDelay * SAMPLING_RATE / 10) * 2;
                int _posToSeek = (int)(_storeStream.Position - _echoOffset);
                if (_posToSeek < 0) _posToSeek = (BUFFER_SIZE + _posToSeek);
                _EchoStream.Seek(_posToSeek, SeekOrigin.Begin);
            }
        }
        
        private void SetModulationType(ModulationType value)
        {
            byte[] _newWaveForm = this.BuildModulationWaveTable(value,_modulationLevel);
            lock (this._ModulationStream)
            {
                _ModulationStream.Seek(0, SeekOrigin.Begin);
                _ModulationStream.Write(_newWaveForm, 0, _newWaveForm.Length);
            }
        }
        private void SetModulationLevel(int value)
        {
            _modulationLevel = value;
            byte[] _newWaveForm = this.BuildModulationWaveTable(_modulationType,_modulationLevel);
            lock (this._ModulationStream)
            {
                _ModulationStream.Seek(0, SeekOrigin.Begin);
                _ModulationStream.Write(_newWaveForm, 0, _newWaveForm.Length);
            }
        }

        public enum ModulationType
        {
            Sinus,
            Square,
            Saw
        }


        public Stream Input { get { return this._ModulationMixer; } }
        public Audio16BufferStream Output { get { return this._OutputStream; } }

        public bool IsEchoEnabled { get { return _EchoMixer.IsEnabled; } set {_EchoMixer.IsEnabled = value;} }        
        public bool IsReverberationEnabled { get { return _ReverberationMixer.IsEnabled; } set { _ReverberationMixer.IsEnabled = value; } }
        public bool IsModulationEnabled { get { return _ModulationMixer.IsEnabled; } set {_ModulationMixer.IsEnabled =value;} }
        public bool IsBalancerEnabled { get { return _balancerMixer.IsEnabled; } set { _balancerMixer.IsEnabled = value; } }
        public int EchoDelay { get { return _echoDelay; } set {SetEchoDelay(value) ;} }
        public int EchoPercentage { get { return _EchoMixer.Percentage; } set {_EchoMixer.Percentage=value;} }        
        public int ReverberationDelay { get { return _ReverberationDelay; } set {SetReverberationDelay(value) ;} }
        public int ReverberationPercentage { get { return _ReverberationMixer.Percentage; } set { _ReverberationMixer.Percentage = value;} }
        public int ModulationSpeed { get { return _ModulationMixer.ModulationSpeed; } set {_ModulationMixer.ModulationSpeed =value;} }
        public ModulationType Modulation { get { return _modulationType; } set {SetModulationType(value);} }
        public int ModulationLevel { get { return _modulationLevel; } set { SetModulationLevel(value); } }
        public int BalancerSpeed { get { return _balancerMixer.ModulationSpeed; } set { _balancerMixer.ModulationSpeed = value; } }

       
    }
}
